/**
 * @file sd_card.c
 * @brief SD卡操作模块实现
 */

#include "sd_card.h"
#include "driver/spi_master.h"
#include "sdmmc_cmd.h"
#include "esp_vfs_fat.h"
#include "esp_log.h"
#include "esp_err.h"
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

#define TAG "SD_CARD"

// 定义sd 卡 SPI引脚
#define PIN_NUM_MISO 19
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK  18
#define PIN_NUM_CS   15

// 文件系统挂载点
#define MOUNT_POINT "/sdcard"

static sdmmc_card_t* card = NULL;

esp_err_t sd_card_init(void)
{
    esp_err_t ret;

    // 初始化SPI总线
    spi_bus_config_t bus_cfg = {
            .miso_io_num = PIN_NUM_MISO,
            .mosi_io_num = PIN_NUM_MOSI,
            .sclk_io_num = PIN_NUM_CLK,
            .quadwp_io_num = -1,
            .quadhd_io_num = -1,
            .max_transfer_sz = 4000,
    };
    ret = spi_bus_initialize(VSPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "SPI总线初始化失败: %d", ret);
        return ret;
    }

    // 配置SD卡主机
    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    host.slot = VSPI_HOST;

    // 配置SPI卡
    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = PIN_NUM_CS;
    slot_config.host_id = host.slot;

    // 挂载FATFS文件系统
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
            .format_if_mount_failed = false,
            .max_files = 5,
            .allocation_unit_size = 16 * 1024
    };

    ret = esp_vfs_fat_sdspi_mount(MOUNT_POINT, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "挂载失败，可能是卡未格式化");
        } else {
            ESP_LOGE(TAG, "SD卡挂载失败: %d", ret);
        }
        spi_bus_free(VSPI_HOST);
        return ret;
    }

    // 打印SD卡信息
    sdmmc_card_print_info(stdout, card);
    ESP_LOGI(TAG, "SD卡初始化成功");

    return ESP_OK;
}

esp_err_t sd_card_deinit(void)
{
    // 卸载文件系统
    esp_err_t ret = esp_vfs_fat_sdcard_unmount(MOUNT_POINT, card);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "卸载文件系统失败: %d", ret);
        return ret;
    }

    // 释放SPI总线
    ret = spi_bus_free(VSPI_HOST);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "释放SPI总线失败: %d", ret);
        return ret;
    }

    card = NULL;
    ESP_LOGI(TAG, "SD卡反初始化成功");

    return ESP_OK;
}

int sd_card_write(const char* path, const void* data, size_t size)
{
    if (data == NULL || size == 0) {
        ESP_LOGE(TAG, "无效的写入参数");
        return -1;
    }

    char full_path[256];
    snprintf(full_path, sizeof(full_path), "%s/%s", MOUNT_POINT, path);

    FILE* f = fopen(full_path, "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "无法打开文件 %s 进行写入", full_path);
        return -1;
    }

    size_t written = fwrite(data, 1, size, f);
    fclose(f);

    if (written != size) {
        ESP_LOGE(TAG, "写入不完整，请求 %zu 字节，实际写入 %zu 字节", size, written);
        return -1;
    }

    ESP_LOGI(TAG, "成功写入 %zu 字节到文件 %s", written, full_path);
    return (int)written;
}

int sd_card_read(const char* path, void* buffer, size_t size)
{
    if (buffer == NULL || size == 0) {
        ESP_LOGE(TAG, "无效的读取参数");
        return -1;
    }

    char full_path[256];
    snprintf(full_path, sizeof(full_path), "%s/%s", MOUNT_POINT, path);

    FILE* f = fopen(full_path, "r");
    if (f == NULL) {
        ESP_LOGE(TAG, "无法打开文件 %s 进行读取", full_path);
        return -1;
    }

    // 获取文件大小
    fseek(f, 0, SEEK_END);
    long file_size = ftell(f);
    rewind(f);

    // 确定要读取的字节数
    size_t bytes_to_read = (size_t)file_size;
    if (bytes_to_read > size) {
        bytes_to_read = size;
        ESP_LOGW(TAG, "缓冲区太小，只能读取文件的前 %zu 字节", bytes_to_read);
    }

    size_t read = fread(buffer, 1, bytes_to_read, f);
    fclose(f);

    ESP_LOGI(TAG, "从文件 %s 读取了 %zu 字节", full_path, read);
    return (int)read;
}

bool sd_card_file_exists(const char* path)
{
    char full_path[256];
    snprintf(full_path, sizeof(full_path), "%s/%s", MOUNT_POINT, path);

    struct stat st;
    if (stat(full_path, &st) == 0) {
        // 文件存在
        return true;
    }

    // 文件不存在
    return false;
}

esp_err_t sd_card_delete(const char* path)
{
    char full_path[256];
    snprintf(full_path, sizeof(full_path), "%s/%s", MOUNT_POINT, path);

    if (unlink(full_path) == 0) {
        ESP_LOGI(TAG, "成功删除文件 %s", full_path);
        return ESP_OK;
    } else {
        ESP_LOGE(TAG, "删除文件 %s 失败", full_path);
        return ESP_FAIL;
    }
}    